Semaphore and CountDownLatch

Semaphore

Semaphore叫做信号量,是一种共享锁,当其permit大于0时,线程可以获取锁,当permits小于0时,线程只能等待获取锁,等其他线程释放。

Semophore也有公平锁和非公平锁两种状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//公平共享锁尝试获取acquires个信号量
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors()) //前面是否有排队,有则返回获取失败
return -1;
int available = getState(); //剩余的信号量(旋转寿司店剩余的座位)
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining)) // 剩余信号量不够,够的情况下尝试获取(旋转寿司店座位不够,或者同时来两对情况抢座位)
return remaining;
}
}
//非公平共享锁尝试获取acquires个信号量
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState(); //剩余的信号量(旋转寿司店剩余的座位)
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining)) // 剩余信号量不够,够的情况下尝试获取(旋转寿司店座位不够,或者同时来两对情侣抢座位)
return remaining;
}
}

CountDownLatch

CountDownLatch用于协调多个线程的同步,能让一个线程在等待其他线程执行完任务后,再继续执行。内部是通过一个计数器去完成实现。

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,可以调用countDown(),计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程(使用await()阻塞)就可以恢复执行任务

使用场景

  • 一个线程执行需要等待其他线程执行完毕,才能继续执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public static void main(String[] args) {
    CountDownLatch latch = new CountDownLatch(10);
    for (int i = 0; i < 10; i++) {
    new Thread(() -> {
    System.out.println(Thread.currentThread().getName() + "---start");
    try {
    Thread.sleep(3000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + "---finish");
    //计数器减一
    latch.countDown();
    }).start();
    }
    try {
    //将主线程阻塞在latch,直到latch的计数器等于0
    latch.await();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("执行完毕-----");
    }

常用方法

CountDownLatch通过state来表示计数器。

  • await() 将当前线程阻塞在CountDownLatch上,直到计数器的数量减少至0.
  • await(long timeout, TimeUnit unit),与await()不同的是,设置了超时等待,等到了时间,不管计数器是不是0,都会继续执行。
  • countDown(),计数器数量减一。当计数器数量减为0时,将await()的线程唤醒。

CyclicBarrier

用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。

和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减 1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。

CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset() 方法可以循环使用,所以它才叫做循环屏障。

CyclicBarrier 有两个构造函数,其中 parties 指示计数器的初始值,barrierAction 在所有线程都到达屏障的时候会执行一次。

img